package edu.utexas.ipc;

import java.util.LinkedList;
import java.util.Queue;

class RAMutex implements Mutex, MessageListener {

	private final int INFINITY = -1;
	private final int processCount;
	private int myts;
	private LamportClock clock;
	private Queue<Integer> pendingQ = new LinkedList<Integer>();
	private int numOkay = 0;
    Connection connection;
    
    public RAMutex(Connection connection, Clock clock, int processCount) {
    	this.myts = INFINITY;
    	this.connection = connection;
    	this.clock = (LamportClock)clock;
    	this.processCount = processCount;
    	
    	// register to receive events
    	connection.registerListener(this);
    }
    
    public synchronized void requestCS() {
    	// send request to all processes
    	myts = clock.getValue();
    	connection.send("request");
    	
    	// wait for OK messages
    	numOkay = 0;
    	while(numOkay < processCount - 1) {
    		myWait();
    	}
    }
    public synchronized void releaseCS() {
        myts = INFINITY;
        while (!pendingQ.isEmpty()) {
            int pid = pendingQ.remove();
            connection.send(pid, "okay");
        }
    }
    
    
    public void messageReceived(Message msg) {
    	
    	int myID = msg.destID;
    	int srcID = msg.sourceID;
        int timeStamp = Integer.parseInt(msg.timestamp);
        
        if (msg.appData.equals("request")) {
            if ((myts == INFINITY) // not interested in CS
                    || (timeStamp < myts)
                    || ((timeStamp == myts) && (srcID < myID))) 	
            	connection.send(srcID, "okay");
            else
                pendingQ.add(srcID);
        } else if (msg.appData.equals("okay")) {
            numOkay++;
            if (numOkay == processCount - 1)
                notify(); // okayCS() may be true now
        }
    }
    
    
	public void acquire() {
		requestCS();
	}

	public void release() {
		releaseCS();
	}

	private void myWait() {
		try {
			wait();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public void close() {
		connection.close();
	}

	public void connectionLost(int processID) {
		// TODO Auto-generated method stub
		
	}
}
